package des.gui;

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import des.algorithm.Encryption.EncryptionType;
import des.handling.FileHandler;
import des.handling.TextHandler;
import des.steganografie.Image_Filter;
import des.steganografie.Steganography;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.regex.Pattern;

public class View extends JPanel {
	public enum Mode {
		FILES, STEGANOGRAPHY
	};

	private static boolean working = false;
	private static Mode mode = Mode.FILES;

	private Steganography stegano;
	private static EncryptionType encryptionType = EncryptionType.DES;

	private static JFrame frame;
	private JLabel lblKey2 = new JLabel();
	private JRadioButton rdbDes = new JRadioButton();
	private JRadioButton rdbTripleDes = new JRadioButton();
	private JTextField txtKey2 = new JTextField();
	private JTextField txtPath = new JTextField();

	private JProgressBar pbBar;
	private JPanel jPanel;
	private JPanel pnlTab;
	private JTextField txtPathStegano;

	File selectedFile, selectedFileStegano;
	private JTextArea txtText;
	private JTabbedPane tab;
	private JLabel lblKey1;
	private JButton btnFile;
	private JButton btnEncrypt;
	private JTextField txtKey1;
	private JButton btnDecrypt;
	private JButton btnFileStegano;
	private JLabel lblStatus;

	// methode om alle controles te enabled/disabled tijden het uitvoeren
	private void setControls(boolean val) {
		tab.setEnabled(val);
		rdbDes.setEnabled(val);
		rdbTripleDes.setEnabled(val);
		txtKey1.setEnabled(val);
		txtKey2.setEnabled(val);
		txtText.setEnabled(val);
		btnEncrypt.setEnabled(val);
		btnDecrypt.setEnabled(val);
		btnFile.setEnabled(val);
		btnFileStegano.setEnabled(val);

	}

	// methode om de keys aan te maken
	private long[] createKeys() {
		long[] keys;

		if (encryptionType == EncryptionType.DES) {
			keys = new long[1];

		} else {
			keys = new long[2];
			keys[1] = TextHandler.createKeyFromPassword(txtKey2.getText());
		}

		keys[0] = TextHandler.createKeyFromPassword(txtKey1.getText());

		return keys;
	}

	// methode om via een regex na te gaan als het wachtwoord aan de eisen
	// voldoen.
	private boolean checkPasswordConstraints() {
		String regexPss = "^((?=.*\\d)(?=.*[a-zA-Z]).{8,})$";
		;

		boolean valid = false;

		if (encryptionType == EncryptionType.DES) {
			if (Pattern.matches(regexPss, txtKey1.getText())) {
				valid = true;
			} else {
				valid = false;
			}

		} else {
			if (Pattern.matches(regexPss, txtKey1.getText())
					&& Pattern.matches(regexPss, txtKey2.getText())) {
				valid = true;
			} else {
				valid = false;
			}
		}

		if (!valid) {
			JOptionPane
					.showMessageDialog(
							frame,
							"Password(s) don't meet the requirements.  Make sure they are at least 8 character and contain at least a digit, small and capital letter");
		}

		return valid;

	}

	// action listener voor de browse knop
	private class BrowseAL implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			JFileChooser fc = new JFileChooser("./");
			fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
			int returnVal = fc.showOpenDialog(frame);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				selectedFile = fc.getSelectedFile();
				txtPath.setText(selectedFile.getPath());
			}
		}
	}

	// action listener voor de browse knop voor steganografie
	private class BrowseSteganoAL implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			JFileChooser fc = new JFileChooser("./");
			fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
			fc.setFileFilter(new Image_Filter());
			int returnVal = fc.showOpenDialog(frame);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				selectedFileStegano = fc.getSelectedFile();
				txtPathStegano.setText(selectedFileStegano.getPath());
			}
		}
	}

	// action listener voor encryptie
	private class EncryptAL implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			working = true;
			if (checkPasswordConstraints()) {

				if (mode == Mode.FILES) {
					// indien in file mode wordt een EncodeFileTask gestart
					setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
					setControls(false);

					EncodeFileTask task = new EncodeFileTask();
					task.execute();

				} else {

					// anders wordt de steganografie uitgevoerd
					long[] keys = createKeys();

					String unencryptedText = txtText.getText();

					String text = TextHandler.encryptText(unencryptedText,
							keys, encryptionType);

					stegano = new Steganography();
					try {
						String ext = Image_Filter
								.getExtension(selectedFileStegano);
						String name = selectedFileStegano.getName();
						String path = selectedFileStegano.getPath();
						path = path.substring(0, path.length() - name.length()
								- 1);
						name = name.substring(0, name.length() - 4);

						String stegan = JOptionPane.showInputDialog(frame,
								"Enter output file name:", "File name",
								JOptionPane.PLAIN_MESSAGE);

						if (stegano.encode(path, name, ext, stegan, text)) {
							JOptionPane
									.showMessageDialog(
											frame,
											"The Image was encoded Successfully!",
											"Success!",
											JOptionPane.INFORMATION_MESSAGE);
						} else {
							JOptionPane.showMessageDialog(frame,
									"The Image could not be encoded!",
									"Error!", JOptionPane.INFORMATION_MESSAGE);
						}
					} catch (Exception except) {
						// msg if opening fails
						JOptionPane.showMessageDialog(frame,
								except.getMessage(), "Error!",
								JOptionPane.INFORMATION_MESSAGE);
					} finally {
						working = false;
						pbBar.setValue(100);
					}
				}

			}

		}

	}

	// action listener voor encryptie
	private class DecryptAL implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			working = true;
			if (checkPasswordConstraints()) {
				// indien files mode: DecodeFileTask
				if (mode == Mode.FILES) {
					setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
					setControls(false);

					DecodeFileTask task = new DecodeFileTask();
					task.execute();
				} else {
					// anders steganografie decode
					String stat_name = null;
					String stat_path = null;

					try {
						// / String image = selectedFileStegano.getPath();
						stat_name = selectedFileStegano.getName();
						stat_path = selectedFileStegano.getPath();
						stat_path = stat_path.substring(0, stat_path.length()
								- stat_name.length() - 1);
						stat_name = stat_name.substring(0,
								stat_name.length() - 4);
						// image_input.setIcon(new ImageIcon(ImageIO.read(new
						// File(image))));
					} catch (Exception except) {
						// msg if opening fails
						JOptionPane.showMessageDialog(frame,
								"The File cannot be opened!", "Error!",
								JOptionPane.INFORMATION_MESSAGE);
					}

					stegano = new Steganography();

					String encryptedText = stegano.decode(stat_path, stat_name);
					System.out.println(stat_path + ", " + stat_name);
					if (encryptedText != "") {

						long[] keys = createKeys();

						String text = TextHandler.decryptText(encryptedText,
								keys, encryptionType);

						txtText.setText(text);
						
						// encode_view();
						JOptionPane.showMessageDialog(frame,
								text,
								"Success!", JOptionPane.INFORMATION_MESSAGE);
					} else {
						JOptionPane.showMessageDialog(frame,
								"The Image could not be decoded!", "Error!",
								JOptionPane.INFORMATION_MESSAGE);
					}
					pbBar.setValue(100);
					working = false;
				}
			}
		}
	}

	// actioon listener om te wijizigen tussen DES en 3DES
	private class RadiobuttonAL implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			String str = e.getActionCommand();

			if (str.equals("DES")) {

				if (rdbDes.isSelected() == true) {
					lblKey2.setVisible(false);
					txtKey2.setVisible(false);
					encryptionType = EncryptionType.DES;
				}
			} else if (str.equals("Triple DES")) {

				if (rdbTripleDes.isSelected() == true) {
					lblKey2.setVisible(true);
					txtKey2.setVisible(true);
					encryptionType = EncryptionType.TRIPLE_DES;
				}

			}
		}

	}

	// listener op tabs om de mode bij te houden
	private class TabsCL implements ChangeListener {
		@Override
		public void stateChanged(ChangeEvent evt) {
			JTabbedPane pane = (JTabbedPane) evt.getSource();

			// Get current tab
			int sel = pane.getSelectedIndex();

			if (sel == 0) {
				mode = Mode.FILES;
			} else {
				mode = Mode.STEGANOGRAPHY;
			}
		}
	}

	// taak om encoding van een file te doen
	class EncodeFileTask extends SwingWorker<Void, Void> {
		// Main task. Executed in background thread.
		@Override
		public Void doInBackground() {
			int time = 0;
			long[] keys = createKeys();

			try {
				Path pathIn = selectedFile.toPath();

				String fileName = pathIn.toString();
				String fileNameOut = fileName + ".DES";
				Path pathOut = FileSystems.getDefault().getPath(fileNameOut);

				File f = pathOut.toFile();
				if (f.exists()) {
					int res = JOptionPane
							.showConfirmDialog(
									frame,
									"The target file already exists, are you sure you want to overwrite?",
									"Alert!", JOptionPane.YES_NO_OPTION,
									JOptionPane.QUESTION_MESSAGE);
					if (res == JOptionPane.YES_OPTION) {

						time = FileHandler.encryptFile(pathIn, pathOut, keys,
								encryptionType, pbBar);
					}
				} else {

					time = FileHandler.encryptFile(pathIn, pathOut, keys,
							encryptionType, pbBar);
				}

				System.out.println(pathIn);
				System.out.println(pathOut);

				lblStatus.setText("Encryption complete in " + time / 1000.0
						+ " seconds.");

			} catch (IOException e) {
				JOptionPane.showMessageDialog(frame, "File could not be read: " + e.getMessage());
			}
			return null;
		}

		@Override
		public void done() {
			setControls(true);
			setCursor(null); // turn off the wait cursor
			working = false;

		}
	}

	// taak om decoding van een file te doen
	class DecodeFileTask extends SwingWorker<Void, Void> {
		// Main task. Executed in background thread.
		@Override
		public Void doInBackground() {
			int time = 0;
			long[] keys = createKeys();

			Path pathIn = selectedFile.toPath();

			String fileName = pathIn.toString();
			String fileNameOut;

			System.out.println(fileName);

			if (fileName.endsWith(".DES")) {
				fileNameOut = fileName.substring(0, fileName.length() - 4);

			} else {
				int res = JOptionPane
						.showConfirmDialog(
								frame,
								"This file does not have a .DES extention, are you sure it is DES encrypted with this program?",
								"Alert!", JOptionPane.YES_NO_OPTION,
								JOptionPane.QUESTION_MESSAGE);
				if (res == JOptionPane.YES_OPTION) {
					String s = JOptionPane.showInputDialog(frame,
							"Choose extension for new file", "Alert !",
							JOptionPane.QUESTION_MESSAGE);

					fileNameOut = fileName + "." + s;
				} else {
					return null;
				}

			}

			Path pathOut = FileSystems.getDefault().getPath(fileNameOut);

			File f = pathOut.toFile();
			if (f.exists()) {
				int res = JOptionPane
						.showConfirmDialog(
								frame,
								"The target file already exists, are you sure you want to overwrite?",
								"Alert!", JOptionPane.YES_NO_OPTION,
								JOptionPane.QUESTION_MESSAGE);
				if (res == JOptionPane.YES_OPTION) {
					try {

						time = FileHandler.decryptFile(pathIn, pathOut, keys,
								encryptionType, pbBar);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						JOptionPane.showMessageDialog(frame, "Error", e.getMessage(), JOptionPane.ERROR_MESSAGE);
					}
				}
			} else {
				try {

					time = FileHandler.decryptFile(pathIn, pathOut, keys,
							encryptionType, pbBar);

				} catch (IOException e) {
					// TODO Auto-generated catch block
					JOptionPane.showMessageDialog(frame, "Error", e.getMessage(), JOptionPane.ERROR_MESSAGE);
				}
			}

			System.out.println(pathIn.toString());
			System.out.println(pathOut.toString());

			lblStatus.setText("Decryption complete in " + time
					+ " milliseconds.");
			return null;
		}

		/*
		 * Executed in event dispatching thread
		 */
		@Override
		public void done() {
			// Toolkit.getDefaultToolkit().beep();
			// startButton.setEnabled(true);

			setControls(true);
			setCursor(null); // turn off the wait cursor
			// taskOutput.append("Done!\n");

			working = false;
		}
	}

	// main methode die het programma opent
	public static void main(String[] args) {
		// Aanpassen LookAndFeel naar Windows theme
		try {
			javax.swing.UIManager.LookAndFeelInfo[] installedLookAndFeels = javax.swing.UIManager
					.getInstalledLookAndFeels();
			for (int idx = 0; idx < installedLookAndFeels.length; idx++) {

				if ("Windows".equals(installedLookAndFeels[idx].getName())) {
					javax.swing.UIManager
							.setLookAndFeel(installedLookAndFeels[idx]
									.getClassName());
				}
			}
		} catch (ClassNotFoundException ex) {
			java.util.logging.Logger.getLogger(View.class.getName()).log(
					java.util.logging.Level.SEVERE, null, ex);
		} catch (InstantiationException ex) {
			java.util.logging.Logger.getLogger(View.class.getName()).log(
					java.util.logging.Level.SEVERE, null, ex);
		} catch (IllegalAccessException ex) {
			java.util.logging.Logger.getLogger(View.class.getName()).log(
					java.util.logging.Level.SEVERE, null, ex);
		} catch (javax.swing.UnsupportedLookAndFeelException ex) {
			java.util.logging.Logger.getLogger(View.class.getName()).log(
					java.util.logging.Level.SEVERE, null, ex);
		}

		// Schedule a job for the event-dispatching thread:
		// creating and showing this application's GUI.
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGUI();
			}
		});
	}

	// methode die GUI aanmaakt om als thread te runnen
	private static void createAndShowGUI() {
		// Create and set up the window.
		frame = new JFrame("DES Project");
		frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

		frame.setSize(new Dimension(493, 589));

		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				exit(frame);
			}
		});
		// Create and set up the content pane.
		JComponent newContentPane = new View();
		newContentPane.setOpaque(true); // content panes must be opaque
		frame.setContentPane(newContentPane);

		// Display the window.
		// frame.pack();
		frame.setVisible(true);
		frame.setResizable(false);
	}

	private static void exit(JFrame f) {
		System.out.println(working);
		int confirmed = JOptionPane.showConfirmDialog(null,
				"Are you sure you want to exit?", "User Confirmation",
				JOptionPane.YES_NO_OPTION);
		if (confirmed == JOptionPane.YES_OPTION) {
			if (working) {
				JOptionPane.showMessageDialog(frame,
						"Encryption in process, can't exit");
			} else {
				frame.dispose();

			}
		}
	}

	// constructor maakt de GUI aan
	public View() {
		super(null);

		jPanel = new JPanel();
		jPanel.setLayout(new GridBagLayout());

		pnlTab = new JPanel();
		pnlTab.setLayout(new GridBagLayout());
		pnlTab.setBounds(new Rectangle(300, 5, 0, 0));

		JPanel pnlProgres = new JPanel();
		pnlProgres.setLayout(null);
		pnlProgres.setBorder(javax.swing.BorderFactory
				.createTitledBorder("Process status"));
		pnlProgres.setBounds(new Rectangle(8, 206, 459, 126));

		JPanel pnlDes = new JPanel();
		pnlDes.setLayout(null);
		pnlDes.setBorder(javax.swing.BorderFactory.createTitledBorder("DES"));
		pnlDes.setBounds(new Rectangle(8, 343, 459, 187));

		pnlDes.add(lblKey2, null);

		JPanel pnlDesMethod = new JPanel();
		pnlDesMethod.setLayout(null);
		pnlDesMethod.setBorder(javax.swing.BorderFactory
				.createTitledBorder("DES Method"));
		pnlDesMethod.setBounds(new Rectangle(15, 25, 123, 83));

		// JLabel
		JLabel jLabel = new JLabel();
		jLabel.setBounds(new Rectangle(33, 27, 293, 16));
		jLabel.setText("Kies je te encrypteren bestand");
		JPanel pnlFile = new JPanel();
		pnlFile.setLayout(null);
		pnlFile.add(jLabel, null);

		lblStatus = new JLabel();
		lblStatus.setBounds(new Rectangle(14, 88, 430, 16));
		lblStatus.setText("Status");

		JLabel jLabel2 = new JLabel();
		jLabel2.setBounds(new Rectangle(16, 27, 437, 16));
		jLabel2.setText("Deze balk geeft de voortgang van de encryptie weer.");

		lblKey1 = new JLabel();
		lblKey1.setBounds(new Rectangle(158, 56, 77, 16));
		lblKey1.setText("DES Key 1");

		lblKey2.setBounds(new Rectangle(157, 81, 80, 16));
		lblKey2.setText("DES Key 2");
		lblKey2.setVisible(false);

		txtKey1 = new JTextField();
		txtKey1.setBounds(new Rectangle(247, 54, 197, 20));

		txtKey2.setBounds(new Rectangle(248, 78, 196, 20));
		txtKey2.setVisible(false);

		txtPath.setBounds(new Rectangle(32, 102, 386, 20));
		txtPath.setText("File Path");
		txtPath.setEnabled(false);

		btnFile = new JButton();
		btnFile.setBounds(new Rectangle(31, 58, 386, 28));
		btnFile.setText("Browse");
		btnFile.addActionListener(new BrowseAL());

		btnEncrypt = new JButton();
		btnEncrypt.setBounds(new Rectangle(14, 146, 122, 23));
		btnEncrypt.setText("Encrypt");
		// btnEncrypt.addActionListener(new MyAction());
		btnEncrypt.addActionListener(new EncryptAL());

		btnDecrypt = new JButton();
		btnDecrypt.setBounds(new Rectangle(316, 145, 122, 23));

		btnDecrypt.setText("Decrypt");
		btnDecrypt.addActionListener(new DecryptAL());

		// JRadioButton
		ButtonGroup buttonGroup1 = new ButtonGroup();

		rdbDes.setSelected(true);
		rdbDes.setBounds(new Rectangle(11, 25, 106, 21));
		rdbDes.setText("DES");
		rdbDes.addActionListener(new RadiobuttonAL());

		rdbTripleDes.setBounds(new Rectangle(11, 51, 107, 21));
		rdbTripleDes.setText("Triple DES");
		rdbTripleDes.addActionListener(new RadiobuttonAL());

		buttonGroup1.add(rdbDes);
		buttonGroup1.add(rdbTripleDes);

		// pnlText //
		JPanel pnlText = new JPanel();
		pnlText.setLayout(null);
		// JLabel
		JLabel jLabel1 = new JLabel();
		jLabel1.setBounds(new Rectangle(5, 5, 431, 16));
		jLabel1.setText("Geef hier je te encrypteren tekst in.");
		txtText = new JTextArea();
		// ScrollPane
		JScrollPane scrolltxt = new JScrollPane(txtText);
		scrolltxt.setBounds(new Rectangle(5, 30, 440, 70));

		btnFileStegano = new JButton();
		btnFileStegano.setBounds(new Rectangle(5, 128, 75, 25));
		btnFileStegano.setText("Browse");
		btnFileStegano.addActionListener(new BrowseSteganoAL());

		// JLabel
		JLabel lblFileStegano = new JLabel();
		lblFileStegano.setText("Kies de foto om het bestand in op te slaan");
		lblFileStegano.setBounds(new Rectangle(5, 110, 400, 10));

		txtPathStegano = new JTextField("File Path");
		txtPathStegano.setBounds(new Rectangle(85, 131, 300, 20));
		txtPathStegano.setEnabled(false);

		pnlText.add(jLabel1, null);
		pnlText.add(scrolltxt, null);
		pnlText.add(btnFileStegano);
		pnlText.add(lblFileStegano);
		pnlText.add(txtPathStegano);

		// JProgressBar
		pbBar = new JProgressBar(0, 100);
		pbBar.setBounds(new Rectangle(15, 57, 428, 21));
		pbBar.setValue(0);
		pbBar.setStringPainted(true);

		tab = new JTabbedPane();
		tab.setBounds(new Rectangle(9, 8, 458, 191));

		tab.addTab("Files", pnlFile);
		tab.addTab("Steganografie", pnlText);
		tab.addChangeListener(new TabsCL());

		pnlFile.add(btnFile, null);
		pnlFile.add(txtPath, null);

		pnlProgres.add(pbBar, null);
		pnlProgres.add(jLabel2, null);
		pnlProgres.add(lblStatus, null);

		pnlDes.add(txtKey1, null);
		pnlDes.add(txtKey2, null);
		pnlDes.add(pnlDesMethod, null);
		pnlDes.add(btnEncrypt, null);
		pnlDes.add(btnDecrypt, null);
		pnlDes.add(lblKey1, null);

		pnlDesMethod.add(rdbDes, null);
		pnlDesMethod.add(rdbTripleDes, null);

		add(pnlTab, null);
		add(tab, null);
		add(pnlProgres, null);
		add(pnlDes, null);
	}

}
